<?php

namespace Backend\Core\Engine;

use Backend\Core\Engine\Model as BackendModel;
use Backend\Core\Language\Language as BackendLanguage;
use SpoonFormButton;
use SpoonFormFile;
use SpoonFormTextarea;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;

/**
 * This is our extended version of \SpoonForm
 */
class Form extends \Common\Core\Form
{
    /**
     * Show the global error
     *
     * @var bool
     */
    private $useGlobalError;

    /**
     * @param string $name           Name of the form.
     * @param string $action         The action (URL) whereto the form will be submitted, if not provided it
     *                               will be autogenerated.
     * @param string $method         The method to use when submitting the form, default is POST.
     * @param bool   $useToken       Should we automagically add a formtoken?
     * @param bool   $useGlobalError Should we automagically show a global error?
     */
    public function __construct(
        string $name = null,
        string $action = null,
        ?string $method = 'post',
        bool $useToken = true,
        bool $useGlobalError = true
    ) {
        $this->useGlobalError = $useGlobalError;
        $url = BackendModel::getContainer()->get('url');

        parent::__construct(
            $name ?? \SpoonFilter::toCamelCase($url->getModule() . '_' . $url->getAction(), '_', true),
            $action,
            $method ?? 'post',
            null,
            $useToken
        );
    }

    /**
     * Add an editor field
     *
     * @param string $name       The name of the element.
     * @param string $value      The value inside the element.
     * @param string $class      Class(es) that will be applied on the element.
     * @param string $classError Class(es) that will be applied on the element when an error occurs.
     * @param bool   $HTML       Will the field contain HTML?
     *
     * @return SpoonFormTextarea
     */
    public function addEditor($name, $value = null, $class = null, $classError = null, $HTML = true): SpoonFormTextarea
    {
        if (BackendModel::getPreferredEditor() !== 'ck-editor') {
            return $this->addTextarea($name, $value, $class, $classError, $HTML);
        }

        if (!$this->header instanceof Header) {
            throw new ServiceNotFoundException('header');
        }

        $name = (string) $name;
        $value = ($value !== null) ? (string) $value : null;
        $class = 'inputEditor ' . (string) $class;
        $classError = 'inputEditorError is-invalid ' . (string) $classError;
        $HTML = (bool) $HTML;

        // we add JS because we need CKEditor
        $this->header->addJS('ckeditor/ckeditor.js', 'Core', false);
        $this->header->addJS('ckeditor/adapters/jquery.js', 'Core', false);
        $this->header->addJS('ckfinder/ckfinder.js', 'Core', false);

        // add the internal link lists-file
        if (is_file(FRONTEND_CACHE_PATH . '/Navigation/editor_link_list_' . BackendLanguage::getWorkingLanguage() . '.js')) {
            $timestamp = @filemtime(
                FRONTEND_CACHE_PATH . '/Navigation/editor_link_list_' . BackendLanguage::getWorkingLanguage() . '.js'
            );
            $this->header->addJS(
                '/src/Frontend/Cache/Navigation/editor_link_list_' . BackendLanguage::getWorkingLanguage(
                ) . '.js?m=' . $timestamp,
                null,
                false,
                true,
                false
            );
        }

        // create and return a textarea for the editor
        return $this->addTextarea($name, $value, $class, $classError, $HTML);
    }

    /**
     * Adds a single file field.
     *
     * @param string $name       Name of the element.
     * @param string $class      Class(es) that will be applied on the element.
     * @param string $classError Class(es) that will be applied on the element when an error occurs.
     *
     * @return SpoonFormFile
     */
    public function addFile($name, $class = null, $classError = null): SpoonFormFile
    {
        $name = (string) $name;
        $class = (string) ($class ?? 'fork-form-file');
        $classError = (string) ($classError ?? 'error form-control-danger is-invalid');

        // add element
        $this->add(new FormFile($name, $class, $classError));

        return $this->getField($name);
    }

    /**
     * Adds a single image field.
     *
     * @param string $name       The name of the element.
     * @param string $class      Class(es) that will be applied on the element.
     * @param string $classError Class(es) that will be applied on the element when an error occurs.
     *
     * @return FormImage
     */
    public function addImage($name, $class = null, $classError = null): FormImage
    {
        $name = (string) $name;
        $class = (string) ($class ?? 'fork-form-image');
        $classError = (string) ($classError ?? 'error form-control-danger is-invalid');

        // add element
        $this->add(new FormImage($name, $class, $classError));

        return $this->getField($name);
    }

    /**
     * Fetches all the values for this form as key/value pairs
     *
     * @param mixed $excluded Which elements should be excluded?
     *
     * @return array
     */
    public function getValues($excluded = ['form', 'save', 'form_token', '_utf8']): array
    {
        return parent::getValues($excluded);
    }

    /**
     * Checks to see if this form has been correctly submitted. Will revalidate by default.
     *
     * @param bool $revalidate Do we need to enforce validation again, even if it might already been done before?
     *
     * @return bool
     */
    public function isCorrect($revalidate = true): bool
    {
        return parent::isCorrect($revalidate);
    }

    /**
     * Parse the form
     *
     * @param TwigTemplate $tpl The template instance wherein the form will be parsed.
     */
    public function parse($tpl): void
    {
        parent::parse($tpl);
        $this->validate();

        // if the form is submitted but there was an error, assign a general error
        if ($this->useGlobalError && $this->isSubmitted() && !$this->isCorrect()) {
            $tpl->assign('formError', true);
        }
    }
}
